package com.excel.service;

import cn.hutool.json.JSONUtil;
import com.excel.constant.RedisConstant;
import com.excel.converter.ObjConverter;
import com.excel.entity.*;
import com.excel.enums.ImportStatusEnum;
import com.excel.enums.ImportTypeEnum;
import com.excel.enums.LanguageTypeEnum;
import com.excel.util.ExcelUtil;
import com.excel.util.LanguageUtil;
import com.excel.util.MinioUtils;
import com.excel.util.StreamUtil;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.*;

/**
 * @ClassName ExportService
 * @Description: 导出服务类
 */
@Slf4j
@Service
public class ImportService {

    @Resource
    private ObjConverter objConverter;

    @Resource
    private AuthenticationService authenticationService;

    @Resource
    private MinioUtils fdsSdk;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private ImportDomainService importDomainService;

    @Lazy
    @Resource
    private ImportService importServiceSelf;

    @Lazy
    @Resource
    private DimensionService dimensionService;

    @Lazy
    @Resource
    private CommonExcelService commonExcelService;


    public void execute() {
        List<ImportInfo> importInfoList = importDomainService.listUnfinished();
        if (CollectionUtils.isEmpty(importInfoList)) {
            return;
        }
        importInfoList.forEach(this::importData);
    }


    public ImportInfo saveImportInfo(MultipartFile file, int totalCount, String parameter, String importType) {
        String path = "import";
        String uploadUrl = "";//fdsSdk.upload(file, path);
        ImportInfo importInfo = new ImportInfo();
        importInfo.setType(importType);
        importInfo.setParameter(parameter);
        importInfo.setSourceFile(uploadUrl);
        importInfo.setTotalCount(totalCount);
        importInfo.init();
        importInfo.setCreateUser(authenticationService.currentUserId());
        importInfo.setCreateTime(System.currentTimeMillis());
        importDomainService.saveImportInfo(importInfo);
        return importInfo;
    }

    public void importData(ImportInfo importInfo) {
        log.warn("导入信息:{}", JSONUtil.toJsonStr(importInfo));
        String status = importInfo.getStatus();
        if (ImportStatusEnum.FINISH.getCode().equals(status)) {
            return;
        }
        // 初始化权限
        Integer importInfoId = importInfo.getId();
        ImportTypeEnum importTypeEnum = ImportTypeEnum.findEnumByType(importInfo.getType());
        String parameter = importInfo.getParameter();
        LanguageTypeEnum languageType = LanguageUtil.getLanguageParams(parameter);
        RLock lock = redissonClient.getLock(RedisConstant.getImportInfoIdKey(importInfoId));
        boolean lockFlag = false;
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            lockFlag = lock.tryLock();
            log.warn("获取锁结果: {}", lockFlag);
            if (lockFlag) {
//                LanguageTypeThreadLocal.setLanguageType(languageType);
                URL url = new URL(importInfo.getSourceFile());
                ImportData importData = getBaseExcelDataList(languageType, importTypeEnum, url.openStream());
                List<? extends BaseExcelData> allExcelDataList = importData.getImportExcelDataList();
                // 执行开始
                executeStartEvent(importTypeEnum, allExcelDataList, importInfo);

                int totalCount = allExcelDataList.size();
                List<? extends BaseExcelData> excelDataList = allExcelDataList.subList(
                        importInfo.getCurLineNo(), totalCount);
                List<ImportInfoFailReason> importInfoFailReasonList =
                        importDomainService.listImportInfoFailReason(importInfoId);
                for (BaseExcelData excelData : excelDataList) {
                    ImportInfoFailReason importInfoFailReason =
                            importServiceSelf.checkAndSaveExcelData(importInfo, parameter, importTypeEnum, excelData);
                    importInfoFailReasonList.add(importInfoFailReason);
                }
                Map<Integer, String> failReasonMap = StreamUtil.toMap(
                        importInfoFailReasonList, ImportInfoFailReason::getLineNo, ImportInfoFailReason::getFailReason);
                Integer successCount = 0;
                Integer failCount = 0;
                for (int i = 0; i < totalCount; i++) {
                    String failReason = failReasonMap.getOrDefault(i, null);
                    log.warn("导入的结果 failReason ： {}", failReason);
                    String result;
                    if (StringUtils.isBlank(failReason)) {
                        successCount++;
                        result = "导入成功";//NeptuneUtil.getLanguageContent(languageType, "导入成功");
                    } else {
                        failCount++;
                        result = "导入失败" + failReason;
//                                NeptuneUtil.getLanguageContent(languageType, "导入失败") + ","
//                                + NeptuneUtil.getLanguageContent(languageType, failReason);
                    }
                    allExcelDataList.get(i).setResult(result);
                }
                Class<?> excelDataImportClass = getExcelDataImportClass(importTypeEnum, languageType);
                ExcelUtil.export(out, Collections.emptyList(), excelDataImportClass, allExcelDataList,importTypeEnum.getCheckFileSheetName());
//                        NeptuneUtil.getLanguageContent(languageType, importTypeEnum.getCheckFileSheetName()));

//                InputStream inputStream = new ByteArrayInputStream(out.toByteArray());
//                FDSObjectMetadata fdsObjectMetadata = new FDSObjectMetadata();
//                String fileName = importInfoId + CommonConstant.UNDERLINE + System.currentTimeMillis()
//                        + CommonConstant.FILE_XLSX_SUFFIX;
//                String filePublic = fdsSdk.uploadFilePublic(fileName, "import", inputStream, fdsObjectMetadata);
//                String checkFile = fdsSdk.getFileUrl(filePublic);
                importInfo.setSuccessCount(successCount);
                importInfo.setFailCount(failCount);
//                importInfo.setCheckFile(checkFile);
                importInfo.setStatus(ImportStatusEnum.FINISH.getCode());
                // 发送导入成功邮件
                sendImportMail(importInfo, importTypeEnum);
                importDomainService.updateImportInfo(importInfo);
                // 执行后续操作
                executeFinishEvent(importTypeEnum, importInfo);
            }
        } catch (Exception e) {
            log.error("op=importData | desc=系统异常 {}", e);
        } finally {
            if (lockFlag) {
                lock.unlock();
            }
//            LanguageTypeThreadLocal.remove();
        }
    }

    private void executeFinishEvent(ImportTypeEnum importTypeEnum, ImportInfo importInfo) {
        // designed to be extended but noop in standard case
    }

    private void executeStartEvent(ImportTypeEnum importTypeEnum, List<? extends BaseExcelData> allExcelDataList, ImportInfo importInfo) {
        // designed to be extended but noop in standard case
    }

    private static final ImmutableMap<ImportTypeEnum, Class<?>> ZH_CN_MAP = ImmutableMap.<ImportTypeEnum, Class<?>>builder()
            .put(ImportTypeEnum.DIMENSION_IMPORT, BaseExcelData.class)
//            .put(ImportTypeEnum.EVALUATE_OBJ_IMPORT, EvaluateObjImportExcelData.class)
            .build();

    private static final ImmutableMap<ImportTypeEnum, Class<?>> EN_US_MAP = ImmutableMap.<ImportTypeEnum, Class<?>>builder()
//            .put(ImportTypeEnum.DIMENSION_IMPORT, DimensionImportExcelDataEn.class)
//            .put(ImportTypeEnum.SUBJECT_IMPORT_EDIT, SubjectImportEditDetailEn.class)
//            .put(ImportTypeEnum.SUBJECT_IMPORT_DEL, SubjectImportDelDetailEn.class)
//            .put(ImportTypeEnum.SUBJECT_IMPORT_ADD, SubjectImportAddDetailEn.class)
//            .put(ImportTypeEnum.EVALUATE_OBJ_IMPORT, EvaluateObjImportExcelEnData.class)
//            .put(ImportTypeEnum.EVALUATE_OBJ_EVAL_RELATION_IMPORT, EvaluateObjEvalRelationExcelEnData.class)
//            .put(ImportTypeEnum.EVALUATE_RELATION_IMPORT, EvalRelationExcelEnData.class)
//            .put(ImportTypeEnum.DIMENSION_DETAIL_INSERT_IMPORT, DimensionDetailInsertImportExcelDataEn.class)
//            .put(ImportTypeEnum.DIMENSION_DETAIL_UPDATE_IMPORT, DimensionDetailUpdateImportExcelDataEn.class)
            .build();

    private static final ImmutableMap<ImportTypeEnum, Class<?>> IMPORT_ERROR_MAP = ImmutableMap.<ImportTypeEnum, Class<?>>builder()
            .put(ImportTypeEnum.DIMENSION_IMPORT, DimensionService.class)
            .put(ImportTypeEnum.USER_AUTH, CommonExcelService.class)
            .build();

    private Class<?> getExcelDataImportClass(ImportTypeEnum importTypeEnum, LanguageTypeEnum languageType) {
        if (Objects.isNull(languageType)) {
            languageType = LanguageTypeEnum.ZH_CN;
        }

        Map<ImportTypeEnum, Class<?>> mapToUse = (languageType == LanguageTypeEnum.EN_US) ? EN_US_MAP : ZH_CN_MAP;
        return mapToUse.getOrDefault(importTypeEnum, null);
    }

    private ImportData getBaseExcelDataList(LanguageTypeEnum languageType, ImportTypeEnum importTypeEnum, InputStream in) {
        if (Objects.isNull(languageType)) {
            languageType = LanguageTypeEnum.ZH_CN;
        }
        switch (importTypeEnum) {
            case DIMENSION_IMPORT:
                return commonExcelService.getCommonExcelData(in, languageType);
//            case USER_AUTH:
//                return authorityService.getAuthorityExcelData(in, languageType);
//            case SUBJECT_IMPORT_EDIT:
//                return subjectService.getSubjectExcelEditData(in, languageType);
//            case SUBJECT_IMPORT_DEL:
//                return subjectService.getSubjectExcelDeleteImportData(in, languageType);
//            case SUBJECT_IMPORT_ADD:
//                return subjectService.getSubjectExcelAddData(in, languageType);
//            case EVALUATE_OBJ_IMPORT:
//                return evaluateObjService.getEvaluateExcelData(in, languageType);
//            case EVALUATE_OBJ_EVAL_RELATION_IMPORT:
//                return evaluateObjService.getEvaluateObjEvalRelationExcelData(in, languageType);
//            case EVALUATE_RELATION_IMPORT:
//                return evalRelationService.getEvaluateRelationExcelData(in, languageType);
//            case DIMENSION_DETAIL_INSERT_IMPORT:
//                return dimensionDescribeService.getDimensionDetailInsertData(in, languageType);
//            case DIMENSION_DETAIL_UPDATE_IMPORT:
//                return dimensionDescribeService.getDimensionDetailUpdateData(in, languageType);
            default:
                return new ImportData(Collections.emptyList());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public ImportInfoFailReason checkAndSaveExcelData(
            ImportInfo importInfo, String parameter, ImportTypeEnum importTypeEnum, BaseExcelData excelData) {
        ImportInfoFailReason importInfoFailReason = new ImportInfoFailReason();
        importInfoFailReason.setImportInfoId(importInfo.getId());
        importInfoFailReason.setLineNo(importInfo.getCurLineNo());
        importInfoFailReason.setFailReason(StringUtils.EMPTY);
        List<String> errors = new ArrayList<>();
        try {
            importServiceSelf.getImportErrorEnums(parameter, importTypeEnum, excelData, importInfo, errors);
        } catch (Exception e) {
            log.error("op=checkAndSaveExcelData | desc=保存单条Excel异常", e);
        }
        if (CollectionUtils.isNotEmpty(errors)) {
            importInfoFailReason.setFailReason(String.join(";", errors));
            importDomainService.saveImportInfoFail(importInfoFailReason);
        }
        importInfo.increaseCurLineNo();
        importDomainService.updateImportInfo(importInfo);
        return importInfoFailReason;
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)
    public void getImportErrorEnums(String parameter, ImportTypeEnum importTypeEnum,
                                    BaseExcelData excelData, ImportInfo importInfo,
                                    List<String> errors) {
        try {
            String createUser = importInfo.getCreateUser();
            String methodName = "checkAndSaveExcelData";
            Object[] args = new Object[]{excelData, createUser, parameter};
            Class<?> excelServiceClass = IMPORT_ERROR_MAP.get(importTypeEnum);
            java.lang.reflect.Method method = excelServiceClass.getMethod(methodName);
            Object result = method.invoke(excelServiceClass, args);
            if (result instanceof List<?>) {
                errors.addAll((List<String>) result);
            }
//            ReflectUtil.invoke(testClass, "setA", 10);
        } catch (Exception e) {
            log.error("e:{}", e);
        }
//        switch (importTypeEnum) {
//            case DIMENSION_IMPORT:
//                errors.addAll(dimensionService.checkAndSaveExcelData(excelData, createUser, parameter));
//                break;
//            case USER_AUTH:
//                errors.addAll(commonExcelService.checkAndSaveExcelData(excelData, createUser, parameter));
//                break;
//            default:
//                break;
//        }
    }

    public ImportInfoVO detail(Integer id) {
        String loginUserId = authenticationService.currentUserId();
        ImportInfo importInfo = importDomainService.getImportInfoById(id);
        String creator = Optional.ofNullable(importInfo).map(ImportInfo::getCreateUser).orElse(Strings.EMPTY);
        if (!loginUserId.equals(creator)) {
            return null;
        }
        return objConverter.convertImportInfoVO(importInfo);
    }

    public void checkImportHead(InputStream inputStream, ImportTypeEnum importTypeEnum) {
        try {
            Dict dict = new Dict();
//                    supportDomainService.selectDictByTypeCodes(
//                    DictTypeEnum.IMPORT_TEMPLATE_URL.getCode(),
//                    importTypeEnum.getTemplateCode()
//            );
            if (Objects.isNull(dict)) {
                throw new RuntimeException("CustomErrorCodes.COMMON_IMPORT_TEMPLATE_ERROR");
            }
            DictExtend ext = dict.getExt();
            if (Objects.isNull(ext)) {
                throw new RuntimeException("CustomErrorCodes.COMMON_IMPORT_TEMPLATE_ERROR");
            }
            Integer sheetAt = ext.getSheetAt();
            Integer titleRows = ext.getTitleRows();
            Integer headerRows = ext.getHeaderRows();
            URL url;
            LanguageTypeEnum languageType = LanguageTypeEnum.ZH_CN;//NeptuneUtil.getCurrentLanguageType();
            if (languageType.equals(LanguageTypeEnum.ZH_CN)) {
                url = new URL(dict.getDictDesc());
            } else {
                url = new URL(dict.getDictDescEn());
            }
            List<List<String>> templateHeadRowList =
                    ExcelUtil.readHead(url.openStream(), sheetAt, titleRows, headerRows);
            List<List<String>> importHeadRowList =
                    ExcelUtil.readHead(inputStream, sheetAt, titleRows, headerRows);
            for (int i = 0; i < headerRows; i++) {
                List<String> templateHeadRow = templateHeadRowList.get(i);
                List<String> importHeadRow = importHeadRowList.get(i);
                List<String> tmpTemplateHeadRow = new ArrayList<>(templateHeadRow);
                tmpTemplateHeadRow.removeAll(importHeadRow);
                boolean missRequiredField = tmpTemplateHeadRow.stream().anyMatch(field -> field.startsWith("*"));
                if (missRequiredField) {
                    throw new RuntimeException("CustomErrorCodes.COMMON_IMPORT_TEMPLATE_MISS_REQUIRED_FIELD");
                }
                List<String> tmpImportHeadRow = new ArrayList<>(importHeadRow);
                tmpImportHeadRow.removeAll(templateHeadRow);
                if (CollectionUtils.isNotEmpty(tmpImportHeadRow)) {
                    throw new RuntimeException("CustomErrorCodes.COMMON_IMPORT_TEMPLATE_FIELD_NOT_MATCH");
                }
            }
        } catch (RuntimeException e) {
            log.error("op=checkImportHead | desc=校验导入模板异常{}", e);
            throw e;
        } catch (Exception e) {
            log.error("op=checkImportHead | desc=校验导入模板异常{}", e);
            throw new RuntimeException("CustomErrorCodes.COMMON_IMPORT_TEMPLATE_ERROR");
        }
    }

    public void sendImportMail(ImportInfo importInfo, ImportTypeEnum importTypeEnum) {
//        String createUser = importInfo.getCreateUser();
//        User user = supportDomainService.getUserByUserId(createUser);
//        if (Objects.isNull(user)) {
//            log.info("op=sendImportMail | desc=用户未找到 | creator={}", createUser);
//            return;
//        }
//        String email = user.getEmail();
//        if (StringUtils.isBlank(email)) {
//            log.info("op=sendImportMail | desc=用户邮箱为空 | creator={}", createUser);
//            return;
//        }
//        String instructions = importTypeEnum.getInstructions();
//        MailMsgDTO mailParam = new MailMsgDTO();
//        mailParam.setContent(instructions + "完成，共计 " + importInfo.getTotalCount() + " 条数据，导入成功 " +
//                importInfo.getSuccessCount() + " 条数据，失败 " + importInfo.getFailCount() + " 条数据，详见附件");
//        mailParam.setReceivers(email);
//        mailParam.setAttaches(Collections.singletonList(importInfo.getCheckFile()));
//        mailParam.setSubject(instructions + "通知");
//        umsSdk.sendMailMsg(mailParam);
    }

}